#include "StdAfx.h"
#include "Number.h"
#include "LookUp.h"


Number::Number(void)
{
	Init();
}


Number::~Number(void)
{
	free(_data);
	_data = NULL;
}


void Number::Init()
{
	_data = (uchar*)malloc(sizeof(char)*MAX_DATA_SIZE);

	if (_data == NULL)
	{
		//crash;
	}
	
	//_id = 0;
    _memLength = MAX_DATA_SIZE;
	_dataLength = 1;
	_exponent = 0;
	_sign = 0;

	_data[0] = 0;

	//_minPrecision;
	//_delta;
}

Number* Number::Add(Number* n2)
{
	Number* result = new Number();
	result->Init();

	if (this->_sign == 0)
	{
		result->CopyFrom(n2);
		return result;
	}
	if (n2->_sign == 0)
	{
		result->CopyFrom(this);
		return result;
	}
	
	Number *work1 = new Number();
	work1->CopyFrom(this);

	Number *work2 = new Number();
	work2->CopyFrom(n2);

	if(work1->_exponent == work2->_exponent)
	{
		work1->Scale(2);
		work2->Scale(2);
	}
	else
	{
		if (work1->_exponent > work2->_exponent)
		{
			work1->Scale(2);
			work2->Scale(work1->_exponent - work2->_exponent + 2);
		}
		else
		{
			work1->Scale(work2->_exponent - work1->_exponent + 2);
			work2->Scale(2);
		}
	}
	
	int numDigits1 = work1->_dataLength;
	int numDigits2 = work2->_dataLength;

	if (numDigits1 >=numDigits2)
	{
		result->CopyFrom(work1);
		int addIndex = (numDigits2 + 1) >> 1;
		int carry = 0;

		while(true)
		{
			addIndex--;
			result->_data[addIndex] += carry + work2->_data[addIndex];

			if (result->_data[addIndex] >= 100)
			{
				result->_data[addIndex] -= 100;
				carry = 1;
			}
			else carry = 0;

			if (addIndex == 0) break;
		}
	}
	else
	{
		result->CopyFrom(work2);
		int addIndex = (numDigits2 + 1) >> 1;
		int carry = 0;

		while(true)
		{
			addIndex--;
			result->_data[addIndex] += carry + work1->_data[addIndex];

			if (result->_data[addIndex] >= 100)
			{
				result->_data[addIndex] -= 100;
				carry = 1;
			}
			else carry = 0;

			if (addIndex == 0) break;
		}
	}
	
	result->_sign = 1;

	result->Normalize();
	delete work1;
	delete work2;

	return result;
}

void Number::Scale(int totalCount)
{
	uchar div, rem, nextDiv;

	uchar* tempData = 0;
	uint count = totalCount;
	uint newLength = (_dataLength + count + 1) >> 1;


	if (newLength > _memLength)
	{
		//realloc or die
	}

	//Moving odd number?
	if ((count & 1) != 0)
	{
		count--;
		tempData = _data;
		int currentPointer = ((_dataLength + 1) >> 1) - 1;

		if ((_dataLength & 1) == 0)//dataLength is even
		{
			char div = 0;

			for (;currentPointer >=0; currentPointer--)
			{
				
				nextDiv = LookUp::NDiv10(currentPointer);
				rem = LookUp::NRem10(currentPointer);

				tempData[currentPointer + 1] = 10*rem + div;
				div = nextDiv;

			}
			tempData[0] = nextDiv;
		}
		else //datalength is odd
		{
			div = LookUp::NDiv10(currentPointer);
			rem = LookUp::NRem10(currentPointer);

			if (currentPointer == 0) tempData[0] = div;
			else
			{
				for (;currentPointer>=0;currentPointer--)
				{
					nextDiv = LookUp::NDiv10(currentPointer);
					rem = LookUp::NRem10(currentPointer);

					tempData[currentPointer] = 10*rem + div;
					div = nextDiv;
				}
			}

		}
		_exponent++;
		_dataLength++;
	}


	if (count > 0)
	{
		int moveLength = (_dataLength + 1) >> 1;
		int scaleBy = count >>1;
		memmove((_data + scaleBy), _data, moveLength);
		memset(_data, 0, scaleBy);
		
		_dataLength += count;
		_exponent += count;
	}
}

void Number::Normalize()
{
	uchar* tempData;
	uchar div, rem, nextRem;
	int index, startIndex;

	if (_sign == 0) return;

	int workingDataLength = _dataLength;
	int workingExponent = _exponent;

	Pad(workingDataLength + 3);

	while (true)
	{
		div = LookUp::NDiv10(_data[0]);
		rem = LookUp::NRem10(_data[0]);

		if (div >= 1) break;

		index = (workingDataLength + 1) >> 1;

		if (rem == 0)
		{
			startIndex = 0;
			tempData = _data;
			while (true)
			{
				if (*tempData !=0) break;

				tempData++;
				startIndex++;
			}

			memmove(_data, tempData, index - startIndex + 1);
			workingDataLength -= 2*startIndex;
			workingExponent -= 2*startIndex;
		}
		else
		{
			for (startIndex = 0; startIndex < index; startIndex++)
			{
				div = LookUp::NDiv10(_data[startIndex + 1]);
				nextRem = LookUp::NRem10(_data[startIndex + 1]);

				_data[startIndex] = 10*rem + div;

				rem = nextRem;
			}
			workingDataLength--;
			workingExponent--;
		}
	}

	while (true)
	{
		index = ((workingDataLength + 1) >> 1) - 1;
		if ((workingDataLength & 1) == 0)
		{
			tempData = _data + index;
			if (*tempData == 0)
			{
				while (true)
				{
					workingDataLength -=2;
					index--;
					tempData--;

					if (*tempData != 0) break;
				}
			}
		}
		div = LookUp::NDiv10(_data[index]);
		rem = LookUp::NRem10(_data[index]);

		if (rem != 0) break;

		if ((workingDataLength & 1) != 0)
		{
			if (div != 0) break;
		}

		if (workingDataLength == 1)
		{
			_sign = 0;
			workingExponent = 0;
			break;
		}
		workingDataLength--;
	}

	_dataLength = workingDataLength;
	_exponent = workingExponent;

}

void Number::Pad(uint count)
{
	uchar div, rem;

	if (_dataLength >= count) return;

	uint padAmount = (count + 1) >> 1;

	if (padAmount > _memLength)
	{
		//realoc or die
	}

	int topIndex = (_dataLength + 1) >> 1;

	if ((_dataLength & 1) != 0)
	{
		div = LookUp::NDiv10(_data[topIndex - 1]);
		rem = LookUp::NRem10(_data[topIndex - 1]);

		_data[topIndex - 1] = 10*div;
	}

	memset(_data + topIndex, 0, padAmount - topIndex);
	_dataLength = count;
}


void Number::CopyFrom(const Number& n2)
{
	uint length = (n2._dataLength + 1) >> 1;
	if (length > _memLength)
	{
		//realoc or die
	}

	_dataLength = n2._dataLength;
	_exponent = n2._exponent;
	_sign = n2._sign;
	memcpy(_data, n2._data, length);
}

void Number::CopyFrom(const Number* n2)
{
	uint length = (n2->_dataLength + 1) >> 1;
	if (length > _memLength)
	{
		//realoc or die
	}

	_dataLength = n2->_dataLength;
	_exponent = n2->_exponent;
	_sign = n2->_sign;
	memcpy(_data, n2->_data, length);
}

Number::Number(long i)
{
	Init();
	Set(i);
}

void Number::Set(long num)
{
	char buf2[64];
	char *buf, *p;
	char c;

	if (num == 0)
	{
		Init();
		return;
	}

	sprintf(buf2, "%ld", num);

	buf = buf2;

	if (num < 0)
	{
		_sign = -1;
		buf++;
	}
	else
	{
		_sign = 1;
	}

	int len = strlen(buf);
	_exponent = len;

	if ((len & 1) != 0) 
		buf[len] = '0';

	while (true)
	{
		if (buf[--len] != '0') break;
	}

	_dataLength = ++len;
	int nbytes = (len + 1) >> 1;
	p = buf;
	for (int index = 0; index < nbytes; index++)
	{
		c = *p++ - '0';
		_data[index] = 10*c + *p++ - '0';
	}
}

void Number::Set(char* numStr)
{
}



void Number::ToString(int decimalPlaces, char* result)
{
	char *currentChar;

	uchar div, rem;

	if(_sign == 0)
	{
		if (decimalPlaces < 0 ) strcpy(result, "0.0E+0");
		else
		{
			strcpy(result, "0");

			if (decimalPlaces > 0) strcat(result, ".");

			for (int i = 0; i < decimalPlaces; i++) strcat(result, "0");

			strcat(result,"E+0");
		}

		return;
	}

	int numDigits;
	int maxIndex = (_dataLength + 1) >> 1;
	if (decimalPlaces < 0)	numDigits = _dataLength;
	else					numDigits = decimalPlaces + 1;

	currentChar = result;
	if (_sign == -1) currentChar = "-";

	bool first = true;

	int stringIndex = 0;
	int dataIndex = 0;

	while (true)
	{
		if (dataIndex >= maxIndex) div = rem = 0;
		else
		{
			div = LookUp::NDiv10(_data[dataIndex]);
			rem = LookUp::NRem10(_data[dataIndex]);
		}

		dataIndex++;

		*currentChar++ = div + '0';

		if (++stringIndex == numDigits) break;

		if (first)
		{
			first = false;
			*currentChar++ = '.';
		}

		*currentChar++ = rem + '0';

		if (++stringIndex == numDigits) break;
	}

	int exponent = _exponent - 1;
	if ( stringIndex >= 0)
		sprintf(currentChar,"E+%d",exponent);
	else
		sprintf(currentChar,"E%d",exponent);


	return;
}